use crate::db::dao::candidate_path_segment_list::CandidatePathSegmentListDao;
use crate::db::{dao, po};
use crate::dto;
use crate::service::Error;
use sea_orm::IntoSimpleExpr;
use std::sync::Arc;
use wanmesh_common::error::ResponseError;

#[async_trait::async_trait]
pub trait CandidatePathSegmentListService: Sync + Send {
    async fn add(
        &self,
        params: &dto::core::candidate_path_segment_list::AddParams,
    ) -> Result<(), Error>;

    async fn list(
        &self,
        filter: &dto::core::candidate_path_segment_list::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::core::candidate_path_segment_list::CandidatePathSegmentList>, Error>;

    async fn all(
        &self,
        candidate_path_id: &str,
    ) -> Result<Vec<dto::core::candidate_path_segment_list::CandidatePathSegmentList>, Error>;

    async fn delete_by_filter(
        &self,
        filter: &dto::core::candidate_path_segment_list::Filter,
    ) -> Result<(), Error>;
}

pub struct DefaultCandidatePathSegmentListService {
    candidate_path_segment_list_dao: Arc<dyn CandidatePathSegmentListDao>,
}

impl DefaultCandidatePathSegmentListService {
    pub fn new(candidate_path_segment_list_dao: Arc<dyn CandidatePathSegmentListDao>) -> Self {
        DefaultCandidatePathSegmentListService {
            candidate_path_segment_list_dao,
        }
    }
}

#[async_trait::async_trait]
impl CandidatePathSegmentListService for DefaultCandidatePathSegmentListService {
    async fn add(
        &self,
        params: &dto::core::candidate_path_segment_list::AddParams,
    ) -> Result<(), Error> {
        self.candidate_path_segment_list_dao
            .insert(po::candidate_path_segment_list::ActiveModel {
                id: Default::default(),
                candidate_path_id: po::ActiveValue::Set(params.candidate_path_id.clone()),
                segment_list_id: po::ActiveValue::Set(params.segment_list_id.clone()),
                weight: po::ActiveValue::Set(params.weight),
                create_timestamp_millis: Default::default(),
                update_timestamp_millis: Default::default(),
            })
            .await?;

        Ok(())
    }

    async fn list(
        &self,
        filter: &dto::core::candidate_path_segment_list::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::core::candidate_path_segment_list::CandidatePathSegmentList>, Error> {
        let po_list = self
            .candidate_path_segment_list_dao
            .find(dao::FindParams {
                filter: filter.into(),
                offset: Some(offset),
                limit: Some(limit),
                ..Default::default()
            })
            .await?;
        let list = po_list.into_iter().map(|po| po.into()).collect();
        Ok(list)
    }

    async fn all(
        &self,
        candidate_path_id: &str,
    ) -> Result<Vec<dto::core::candidate_path_segment_list::CandidatePathSegmentList>, Error> {
        let filter = dao::candidate_path_segment_list::Filter {
            candidate_path_id: Some(candidate_path_id.to_string()),
            ..Default::default()
        };
        let po_list = self
            .candidate_path_segment_list_dao
            .find(dao::FindParams {
                filter,
                order_by: Some(dao::OrderBy {
                    column: po::candidate_path_segment_list::Column::Weight.into_simple_expr(),
                    order: sea_orm::Order::Desc,
                }),
                ..Default::default()
            })
            .await?;
        let list = po_list.into_iter().map(|po| po.into()).collect();
        Ok(list)
    }

    async fn delete_by_filter(
        &self,
        filter: &dto::core::candidate_path_segment_list::Filter,
    ) -> Result<(), Error> {
        if filter == &Default::default() {
            return Err(ResponseError::filter_can_not_be_empty().into());
        }

        self.candidate_path_segment_list_dao
            .delete(filter.into())
            .await?;

        Ok(())
    }
}

impl From<&dto::core::candidate_path_segment_list::Filter>
    for dao::candidate_path_segment_list::Filter
{
    fn from(value: &dto::core::candidate_path_segment_list::Filter) -> Self {
        Self {
            candidate_path_id: value.candidate_path_id.clone(),
            segment_list_id: value.segment_list_id.clone(),
        }
    }
}

impl From<dto::core::candidate_path_segment_list::Filter>
    for dao::candidate_path_segment_list::Filter
{
    fn from(value: dto::core::candidate_path_segment_list::Filter) -> Self {
        Self::from(&value)
    }
}

impl From<&po::candidate_path_segment_list::Model>
    for dto::core::candidate_path_segment_list::CandidatePathSegmentList
{
    fn from(value: &po::candidate_path_segment_list::Model) -> Self {
        Self {
            candidate_path_id: value.candidate_path_id.clone(),
            segment_list_id: value.segment_list_id.clone(),
            weight: value.weight,
        }
    }
}

impl From<po::candidate_path_segment_list::Model>
    for dto::core::candidate_path_segment_list::CandidatePathSegmentList
{
    fn from(value: po::candidate_path_segment_list::Model) -> Self {
        Self::from(&value)
    }
}
